﻿using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using HardMock.Domain;

namespace HardMock.Recording
{
    class IdentifierFactory : IIdentifierFactory
    {
        private readonly IHashGenerator hashGenerator;

        private IdentifierFactory(IHashGenerator hashGenerator)
        {
            this.hashGenerator = hashGenerator;
        }

        public IdentifierFactory()
            : this(new HashGenerator())
        {
        }

        public string Create(MethodCall methodCall)
        {
            const string separator = " ";
            var identifier = new StringBuilder();

            Append(identifier, methodCall.Assembly, separator);
            Append(identifier, methodCall.Type, separator);
            Append(identifier, methodCall.Name);
            AppendParameters(methodCall, identifier, separator);

            return identifier.ToString();
        }

        private void AppendParameters(MethodCall methodCall, StringBuilder identifier, string separator)
        {
            identifier.Append("(");
            var arguments = methodCall.Arguments.ToList();
            for (var i = 0; i < arguments.Count(); i++)
            {
                identifier.Append(arguments[i].Type);
                identifier.Append(separator);
                identifier.Append(HashArgument(arguments[i]));
                if (i < arguments.Count - 1)
                {
                    identifier.Append(", ");
                }
            }
            identifier.Append(")");
        }

        private static void Append(StringBuilder stringbuilder, object toAdd, string separator = "")
        {
            stringbuilder.Append(toAdd);
            stringbuilder.Append(separator);
        }

        private int HashArgument(Argument argument)
        {
            try
            {
                return hashGenerator.Create(argument.Value);
            }
            catch (SerializationException ex)
            {
                throw new HardMockException(ex.Message);
            }
        }
    }
}